﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using SeguridadGaleria;
using ModeloGaleriaNET;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
using AvalonDock;
using System.Data.Objects.DataClasses;
using System.Windows.Controls.Primitives;

namespace GaleriaNET
{
    /// <summary>
    /// Lógica de interacción para ucGruposRoles.xaml
    /// </summary>
    public partial class ucGruposRoles : UserControl
    {
        private string[] permisosBuscar = new string[] { TiposRoles.CREAR_GRUPO, TiposRoles.DAR_PRIVILEGIO_GRUPO, TiposRoles.QUITAR_PRIVILEGIO_GRUPO};
        private readonly string title;
        private bool loaded;
        private readonly int pageSize = int.Parse(CADs.Properties.Resources.PageSize);
        private BackgroundWorker worker, workerActualizar, workerPermisos;
        private ManejadorRoles roleManager;
        private List<GrupoValue> grupos;
        private List<string> permisos;
        private List<object> permisosGrupos;
        private Type generetedType;
        private List<GrupoValue> nuevosGrupos;
        private bool actualizado;
        private Dictionary<string, bool> permisosDatos;
        private IAuditoria auditoria;

        public ucGruposRoles()
        {
            InitializeComponent();
            roleManager = ManejadorRoles.Instance;
            title = "Grupos de usuarios";
            worker = new BackgroundWorker();
            worker.DoWork += worker_DoWork;
            worker.RunWorkerCompleted += worker_RunWorkerCompleted;
            workerActualizar = new BackgroundWorker();
            workerActualizar.DoWork += workerActualizar_DoWork;
            workerActualizar.RunWorkerCompleted += workerActualizar_RunWorkerCompleted;
            workerPermisos = new BackgroundWorker();
            workerPermisos.DoWork += workerPermisos_DoWork;
            workerPermisos.RunWorkerCompleted += workerPermisos_RunWorkerCompleted;
            auditoria = new AuditoriaBD();
        }

        public string Title
        {
            get { return title; }
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            if (!loaded)
            {
                if (!workerPermisos.IsBusy)
                {
                    workerPermisos.RunWorkerAsync();
                    if (!worker.IsBusy)
                    {
                        _busyIndicator.IsBusy = true;
                        worker.RunWorkerAsync();
                    }
                }
                loaded = true;
            }
        }

        private void workerActualizar_DoWork(object sender, DoWorkEventArgs e)
        {
            actualizado = roleManager.ActualizarPermisosGrupo(nuevosGrupos);
        }

        private void workerActualizar_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            int uId;
            string permisosString;

            _busyIndicator.IsBusy = false;
            uId = Convert.ToInt32(Utilities.FindUsuarioData(typeof(UserWindow), this, "Id"));
            if (actualizado)
            {
                foreach (GrupoValue gv in nuevosGrupos)
                {
                    if (gv.PermisosBorrados != null && gv.PermisosBorrados.Length > 0)
                    {
                        permisosString = Utilities.ListToString(gv.PermisosBorrados.ToList(), ", ");
                        auditoria.Mensaje(new object[] {permisosString, gv.Nombre },
                            TiposAuditoria.Ordinal(TiposAuditoria.TIPO_AUDITORIA.QUITAR_PRIVILEGIOS_GRUPO), uId);
                    }

                    if (gv.PermisosNuevos != null && gv.PermisosNuevos.Length > 0)
                    {
                        permisosString = Utilities.ListToString(gv.PermisosNuevos.ToList(), ", ");
                        auditoria.Mensaje(new object[] { permisosString, gv.Nombre },
                            TiposAuditoria.Ordinal(TiposAuditoria.TIPO_AUDITORIA.DAR_PRIVILEGIOS_GRUPO), uId);
                    }
                }

                Actualizar();
            }
            else
            {
                foreach (GrupoValue gv in nuevosGrupos)
                {
                    if (gv.PermisosBorrados != null && gv.PermisosBorrados.Length > 0)
                    {
                        permisosString = Utilities.ListToString(gv.PermisosBorrados.ToList(), ", ");
                        auditoria.Mensaje(new object[] { permisosString, gv.Nombre },
                            TiposAuditoria.Ordinal(TiposAuditoria.TIPO_AUDITORIA.ERROR_QUITAR_PRIVILEGIOS_GRUPO), uId);
                    }

                    if (gv.PermisosNuevos != null && gv.PermisosNuevos.Length > 0)
                    {
                        permisosString = Utilities.ListToString(gv.PermisosNuevos.ToList(), ", ");
                        auditoria.Mensaje(new object[] { permisosString, gv.Nombre },
                            TiposAuditoria.Ordinal(TiposAuditoria.TIPO_AUDITORIA.ERROR_DAR_PRIVILEGIOS_GRUPO), uId);
                    }
                }

                Xceed.Wpf.Toolkit.MessageBox.Show("No se ha podido realizar la actualización de los permisos", "Error",
                    MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void workerPermisos_DoWork(object sender, DoWorkEventArgs e)
        {
            string usuario;
            bool? value;

            permisosDatos = new Dictionary<string, bool>();
            usuario = Convert.ToString(Utilities.FindUsuarioData(typeof(UserWindow), this, "Name"));
            foreach (string s in permisosBuscar)
            {
                value = roleManager.CanUse(s, usuario);
                if (value != null)
                    permisosDatos.Add(s, value.Value);

            }
        }

        private void workerPermisos_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            bool value, value2;
            IntPtr b;
            BitmapSource bs;

            if (!(permisosDatos != null && permisosDatos.Count == permisosBuscar.Length))
            {
                Xceed.Wpf.Toolkit.MessageBox.Show("No se han podido obtener los permisos", "Error",
                    MessageBoxButton.OK, MessageBoxImage.Error);
            }
            else
            {
                permisosDatos.TryGetValue(TiposRoles.CREAR_GRUPO, out value);
                if (value == false)
                {
                    b = Properties.Resources.Agregar_gris_24.GetHbitmap();
                    bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(b, IntPtr.Zero, Int32Rect.Empty,
                      System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
                    this.imgBtNuevo.Source = bs;
                    btNuevo.IsEnabled = false;
                }

                permisosDatos.TryGetValue(TiposRoles.DAR_PRIVILEGIO_GRUPO, out value);
                permisosDatos.TryGetValue(TiposRoles.QUITAR_PRIVILEGIO_GRUPO, out value2);
                if (!(value != false && value2 != false))
                {
                    btGuardar.IsEnabled = false;
                }
            }
        }

        private void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            object generatedObject;
            PropertyInfo[] properties;
            int propertiesCounter;
            bool value;

            while (workerPermisos.IsBusy)
            { }

            grupos = roleManager.GetGrupos();
            permisos = roleManager.GetAllPermisos();

            if (grupos != null && permisos != null)
            {
                permisosGrupos = new List<object>();
                generetedType = GeneradorTipos.Instance.GenerarTipoPermisoGrupos(grupos);

                foreach (string p in permisos)
                {
                    generatedObject = Activator.CreateInstance(generetedType);
                    properties = generetedType.GetProperties();
                    properties[0].SetValue(generatedObject, p, null);
                    propertiesCounter = 1;

                    foreach (GrupoValue g in grupos)
                    {
                        value = g.Permisos.Contains(p);
                        properties[propertiesCounter].SetValue(generatedObject, value, null);
                        propertiesCounter++; 
                    }

                    permisosGrupos.Add(generatedObject);
                }
            }
        }

        private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            DataGridCheckBoxColumn c;
            DataGridTextColumn t;
            bool value = false, value2 = false;

            t = new DataGridTextColumn();
            t.Header = @"Permisos\Grupos";
            t.Binding = new Binding() { Path = new PropertyPath("Permisos", null) };
            t.CanUserReorder = false;
            t.CanUserResize = false;
            t.CanUserSort = true;
            t.IsReadOnly = true;
            t.HeaderStyle = FindResource("ColumnHeaderStyle") as Style;
            dgGrupos.Columns.Add(t);

            if (permisosDatos != null && permisosDatos.Count > 0)
            {
                permisosDatos.TryGetValue(TiposRoles.DAR_PRIVILEGIO_GRUPO, out value);
                permisosDatos.TryGetValue(TiposRoles.QUITAR_PRIVILEGIO_GRUPO, out value2);
            }
            
            foreach (GrupoValue g in grupos)
            {
                c = new DataGridCheckBoxColumn();
                c.Header = g.Nombre;
                c.Binding = new Binding() { Path = new PropertyPath(g.Nombre, null) };
                c.CanUserReorder = false;
                c.CanUserResize = false;
                c.CanUserSort = false;
                c.IsReadOnly = permisosDatos != null && permisosDatos.Count > 0 ? !(value && value2) : true;
                t.HeaderStyle = FindResource("ColumnHeaderStyle") as Style;
                dgGrupos.Columns.Add(c);
            }

            dgGrupos.ItemsSource = new List<object>(permisosGrupos);
            _busyIndicator.IsBusy = false;
        }

        private List<GrupoValue> GetNewGrupoValues()
        {
            List<GrupoValue> nuevosGrupos;
            DataGridRow fila;
            GrupoValue newGrupo;
            string nombreGrupo;
            List<string> newPermisos;
            bool value;
            int items;

            items = dgGrupos.Items.Count;
            nuevosGrupos = new List<GrupoValue>(); 
            foreach (GrupoValue g in grupos)
            {
                newPermisos = new List<string>();
                nombreGrupo = g.Nombre;
                newGrupo = new GrupoValue();
                newGrupo.Nombre = nombreGrupo;

                for (int i = 0; i < items; i++)
                {
                    fila = dgGrupos.ItemContainerGenerator.ContainerFromIndex(i) as DataGridRow;
                    value = Convert.ToBoolean(generetedType.GetProperty(nombreGrupo, BindingFlags.Public | BindingFlags.Instance).
                       GetGetMethod().Invoke(fila.Item, null));

                    if (value)
                        newPermisos.Add(generetedType.GetProperty("Permisos", BindingFlags.Public | BindingFlags.Instance).
                            GetGetMethod().Invoke(fila.Item, null).ToString());
                }

                newGrupo.Permisos = newPermisos.ToArray();
                nuevosGrupos.Add(newGrupo);
            }

            return nuevosGrupos;
        }

        private void btGuardar_Click(object sender, RoutedEventArgs e)
        {
            GrupoValue antiguo;
            int uId;

            dgGrupos.CommitEdit(DataGridEditingUnit.Row, true);
            nuevosGrupos = GetNewGrupoValues();
            uId = Convert.ToInt32(Utilities.FindUsuarioData(typeof(UserWindow), this, "Id"));
            
            // Antes de nada, para cada grupo vemos cuáles son sus permisos nuevos y los que hay que borrar
            foreach (GrupoValue group in nuevosGrupos)
            {
                antiguo = grupos.Where(gv => gv.Nombre.Equals(group.Nombre)).First();
                // Los permisos nuevos son los que están en el grupo y no en antiguos
                group.PermisosNuevos = group.Permisos.Except(antiguo.Permisos).ToArray();
                if (group.PermisosNuevos != null && group.PermisosNuevos.Length > 0)
                {
                    auditoria.Mensaje(new object[] { Utilities.ListToString(group.PermisosNuevos.ToList(), ", "), group.Nombre },
                    TiposAuditoria.Ordinal(TiposAuditoria.TIPO_AUDITORIA.INICIO_DAR_PRIVILEGIOS_GRUPO), uId);
                }

                // Los permisos borrados son los que están en antiguos y no en nuevos
                group.PermisosBorrados = antiguo.Permisos.Except(group.Permisos).ToArray();
                if (group.PermisosBorrados != null && group.PermisosBorrados.Length > 0)
                {
                    auditoria.Mensaje(new object[] { Utilities.ListToString(group.PermisosBorrados.ToList(), ", "), group.Nombre },
                    TiposAuditoria.Ordinal(TiposAuditoria.TIPO_AUDITORIA.INICIO_QUITAR_PRIIVILEGIOS_GRUPO), uId);
                }
            }

            if (!workerActualizar.IsBusy)
            {
                _busyIndicator.IsBusy = true;
                actualizado = false;
                workerActualizar.RunWorkerAsync();
            }
        }

        public void Actualizar()
        {
            if (!worker.IsBusy)
            {
                dgGrupos.ItemsSource = null;
                dgGrupos.Columns.Clear();
                worker.RunWorkerAsync();
            }
        }

        private void btNuevo_Click(object sender, RoutedEventArgs e)
        {
            DocumentContent doc = FindDocumentContent(typeof(ucNuevoGrupo));

            if (doc == null)
                ShowPageInParentDock<ucNuevoGrupo>();
            else
                Xceed.Wpf.Toolkit.MessageBox.Show("Ya hay abierta una ventana de creación de grupos. Ciérrela antes de abrir otra.",
                    "Error", MessageBoxButton.OK, MessageBoxImage.Error);
        }

        private void ShowPageInParentDock<T>(EntityObject obj = null) where T : UserControl
        {
            ucNuevoGrupo control;

            control = new ucNuevoGrupo();
            OpenNewDocument(control);
        }

        private DocumentContent FindDocumentContent(Type t)
        {
            AvalonDock.DockingManager manager;

            manager = ((AvalonDock.DocumentContent)this.Parent).Manager;
            // Buscamos si hay abierta una instancia del documento a abrir
            foreach (AvalonDock.DocumentContent doc in manager.Documents)
            {
                if (doc.Content.GetType() == t)
                    return doc;
            }
            return null;
        }

        private void OpenNewDocument(UserControl c)
        {
            DependencyObject padre;

            try
            {
                padre = this.Parent;
                if (padre is AvalonDock.DocumentContent)
                {
                    padre = ((AvalonDock.DocumentContent)padre).Manager;
                    if (padre is AvalonDock.DockingManager)
                    {
                        padre = ((AvalonDock.DockingManager)padre).Parent;
                        if (padre is Grid)
                        {
                            padre = ((Grid)padre).Parent;

                            if (padre is UserWindow)
                            {
                                if (c is ucNuevoGrupo)
                                    ((UserWindow)padre).ShowPage<ucNuevoGrupo>((ucNuevoGrupo)c);
                            }
                            else
                                throw new ErrorOpeningNewDocumentException("No se puede abrir la nueva ventana");
                        }
                        else
                            throw new ErrorOpeningNewDocumentException("No se puede abrir la nueva ventana");
                    }
                    else
                        throw new ErrorOpeningNewDocumentException("No se puede abrir la nueva ventana");
                }
                else
                    throw new ErrorOpeningNewDocumentException("No se puede abrir la nueva ventana");
            }
            catch (ErrorOpeningNewDocumentException ex)
            {
                Xceed.Wpf.Toolkit.MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }
        }
    }
}
